/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

// 草图 - 圆弧

import {
  BaseCommand,
  CadScene,
  CENTEREVENTS,
  CommandHint,
  CommandHintStatus,
  CSArc,
  CSCircle,
  CSLine,
  css2dInputLable,
  CSys,
  EventsRes,
  MathTool,
  SceneGraph,
  useStore,
} from '@amcax/base';
import { KEYBOARD_KEY } from '@app-cad/common/Constants';
import { ctx2 } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';

import { Picker3D, PickerMgr } from '@amcax/renderer';

import { eventCenterManager } from '@app-base/common/events/EventCenterManager';
import { useEffect, useState } from 'react';
import { Vector2, Vector3, Vector3Tuple } from 'three-legacy';

enum State {
  NONE,
  DRAW_CIRCLE,
  DRAW_ARC,
  FINISH,
}

type MouseState =
  | State.NONE
  | State.DRAW_CIRCLE
  | State.DRAW_ARC
  | State.FINISH;

interface TrackingData {
  circle: CSCircle | null;
  arc: CSArc | null;
  rLine: CSLine | null;
  rLabel: css2dInputLable | null;
}

interface ArcCommandProps {
  title: string;
  onClose: () => void;
}

function ArcCommand({ onClose }: ArcCommandProps) {
  const updateCmdHighlightId = useStore((state) => state.updateCmdHighlightId);
  const [hits, setHits] = useState<string>('');

  const cadScene = ctx2.cadScene as CadScene;
  const sceneSetup = ctx2.viewer.sceneSetup;
  const assisGroup = cadScene.assisGroup;

  const xAxis = new Vector3(1, 0, 0);
  const yAxis = new Vector3(0, 1, 0);
  const zAxis = new Vector3(0, 0, 1);
  type MousePoint = {
    x: number;
    y: number;
  };

  // 默认为 XOY 平面
  let faceCSys: CSys = MathTool.createCsysFromPlacement([
    [0, 0, 0],
    [0, 0, 1],
    [1, 0, 0],
  ]);
  let planeNormal: Vector3 = faceCSys.getNormalVc3();
  let planeOrigin: Vector3 = faceCSys.getOriginVc3();
  let planeName: string = 'XOY';

  let startPoint: Vector3; // 绘制起点：圆心
  let startPoint2D: Vector2; // 圆弧起始点局部
  let centerPoint: Vector3; // 圆心点
  let centerPoint2D: Vector2; // 圆心点局部
  let endPoint: Vector3; // 圆弧的终点
  let endPoint2D: Vector2; // 圆弧终点局部
  let startAngle: number = 0; // 起始角度
  let radius: number = 0; // 圆半径
  let clockWise: boolean | undefined = undefined; // 逆时针与否
  let mouseVW: Vector3;
  let mousePoint: MousePoint = {
    x: 0,
    y: 0,
  };

  let currentStatus: MouseState = State.NONE; // 记录当前状态

  let trackingData: TrackingData = {
    circle: null,
    arc: null,
    rLine: null,
    rLabel: null,
  };

  useEffect(() => {
    // exit
    return () => {
      onDestroy();
    };
  }, []);

  useAsyncEffect(async () => {
    init();
  }, []);

  const init = () => {
    registerEvents();
    changePlaneToXOY();
  };
  // ========= 鼠标和键盘事件=========
  const registerEvents = () => {
    eventCenterManager.activeEventCenter
      .setState('arc')
      .on(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .on(CENTEREVENTS.POINTERUP, onMouseClick)
      .on(CENTEREVENTS.KEYUP, onKeyup)
      .addExit(onDestroy);
  };
  const unregisterEvents = () => {
    eventCenterManager.activeEventCenter
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .off(CENTEREVENTS.POINTERUP, onMouseClick)
      .off(CENTEREVENTS.KEYUP, onKeyup)
      .setDefault();
  };

  const onKeyup = (e: EventsRes<KeyboardEvent>) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      onDestroy();
    } else if (e.key === KEYBOARD_KEY.ENTER) {
      if (currentStatus === State.DRAW_ARC) {
        finishDraw();
        onDestroy();
      }
    } else if (e.key === KEYBOARD_KEY.ARROWUP) {
      changePlaneToXOY();
    } else if (e.key === KEYBOARD_KEY.ARROWRIGHT) {
      changePlaneToYOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWLEFT) {
      changePlaneToXOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWDOWN) {
      changePlaneToXOY();
    }
  };
  const onMouseClick = (e: EventsRes) => {
    if (e.button.onLeft) {
      if (currentStatus === State.NONE) {
        trackingData.arc = null;
        // 鼠标左键，开始绘制圆心
        centerPoint = mouseVW;
        planeOrigin = centerPoint.clone();
        createFaceCSys();
        currentStatus = State.DRAW_CIRCLE;
      } else if (currentStatus === State.DRAW_CIRCLE) {
        currentStatus = State.DRAW_ARC;
        trackingData.rLine = null;
        trackingData.arc = null;
        trackingData.rLabel = null;
        assisGroup.clear();
      } else if (currentStatus === State.DRAW_ARC) {
        currentStatus = State.FINISH;
        // 鼠标左键，完成
        finishDraw();
        onDestroy();
      }
    } else if (e.button.onRight) {
      currentStatus = State.FINISH;
      // 鼠标右键，放弃
      onDestroy();
    }
  };
  const onMouseMove = (e: any) => {
    // 在面上画弧形
    const pickRes = PickerMgr.getPicker<Picker3D>('3d').pick(e, { face: true });
    if (pickRes && pickRes?.assisObj?.csys) {
      planeNormal = pickRes.assisObj.csys?.getNormalVc3();
      planeOrigin = pickRes.assisObj.csys?.getOriginVc3();
    }
    mousePoint.x = e.event.layerX;
    mousePoint.y = e.event.layerY;
    mouseVW = getMouseWorldVector(mousePoint); // 当前鼠标的世界坐标

    if (currentStatus === State.DRAW_CIRCLE) {
      startPoint = mouseVW.clone();
      calcRadius(centerPoint, startPoint);
      preview();
    } else if (currentStatus === State.DRAW_ARC) {
      endPoint = mouseVW.clone();
      preview();
    }
  };
  // ========= 鼠标和键盘事件 =========
  // ========= 位置变换 =========
  const changePlaneToXOY = () => {
    changePlane(zAxis, 'XOY');
  };
  const changePlaneToYOZ = () => {
    changePlane(xAxis, 'YOZ');
  };
  const changePlaneToXOZ = () => {
    changePlane(yAxis, 'XOZ');
  };

  const changePlane = (_axis: Vector3, _planeName: string) => {
    planeName = _planeName;
    planeNormal = _axis.clone();
    createFaceCSys();
    preview();
    setHits(
      '点击确定圆心，当前绘制平面为 ' +
        planeName +
        ' 平面，按方向键切换绘制平面',
    );
  };

  const createFaceCSys = () => {
    let _xAxis: number[] = []; // 局部坐标系的X轴
    let _zAxis: never[] | Vector3Tuple = []; // 局部坐标系的Z轴
    if (planeName === 'XOY') {
      _xAxis = [1, 0, 0];
      _zAxis = zAxis.clone().toArray();
    } else if (planeName === 'YOZ') {
      _xAxis = [0, 0, 1];
      _zAxis = xAxis.clone().toArray();
    } else if (planeName === 'XOZ') {
      _xAxis = [1, 0, 0];
      _zAxis = yAxis.clone().toArray();
    }
    const placement = [planeOrigin.clone().toArray(), _zAxis, _xAxis];
    faceCSys = MathTool.createCsysFromPlacement(placement);
    trackingData.circle && trackingData.circle.resetCsys(faceCSys);
    trackingData.arc && trackingData.arc.resetCsys(faceCSys);
    trackingData.rLine && trackingData.rLine.resetCsys(faceCSys);
    endPoint = getMouseWorldVector(mousePoint);
  };

  const getMouseWorldVector = (mousePoint: MousePoint): Vector3 => {
    const normal = planeNormal.clone();
    const point = planeOrigin.clone();
    const mouseWvec: Vector3 = sceneSetup.mouseOnPlane(
      mousePoint.x,
      mousePoint.y,
      normal,
      point,
    );
    return mouseWvec;
  };

  // 计算半径
  const calcRadius = (start: Vector3, end: Vector3) => {
    centerPoint2D = new Vector2().fromArray(
      faceCSys.inTransformation.apply(start.clone()).toArray(),
    );
    endPoint2D = new Vector2().fromArray(
      faceCSys.inTransformation.apply(end.clone()).toArray(),
    );
    radius = centerPoint2D.clone().distanceTo(endPoint2D.clone());
  };
  const calcPointWithDistance = (
    start: Vector3,
    end: Vector3,
    distance: number,
  ) => {
    const p = end.clone().sub(start).setLength(distance).add(start);
    return p;
  };

  // 画辅助图形
  const drawAssist = (centerPoint: Vector3, endPoint: Vector3) => {
    // 绘制圆半径
    if (!trackingData.rLine) {
      trackingData.rLine = new CSLine({
        vertexs: [centerPoint, endPoint],
        dashed: true,
        lineWidth: 2,
        depthTest: true,
        csys: faceCSys,
      });
      trackingData.rLine.addTo(assisGroup);
    } else {
      trackingData.rLine.update({
        vertexs: [centerPoint, endPoint],
      });
    }

    // draw input label
    let labelText: number;
    let type: string = 'circle';
    if (trackingData.arc) {
      labelText = trackingData.arc.getDegree();
      type = 'angle';
    } else {
      labelText = centerPoint.distanceTo(endPoint);
    }
    const labelPos = new Vector3();
    labelPos.x = (centerPoint.x + endPoint.x) / 2;
    labelPos.y = (centerPoint.y + endPoint.y) / 2;
    labelPos.z = (centerPoint.z + endPoint.z) / 2;
    if (!trackingData.rLabel) {
      trackingData.rLabel = new css2dInputLable({
        text: labelText,
        position: labelPos,
        type: type,
      });
      trackingData.rLabel.focus();
      trackingData.rLabel.addTo(assisGroup);
    } else {
      trackingData.rLabel.update(labelText, labelPos);
    }
  };
  // ========= 位置变换 =========
  // ========= 释放资源 =========
  const onDestroy = () => {
    unregisterEvents();
    releaseObjects();
    resetParams();
    onClose();
  };
  // 释放对象
  const releaseObjects = () => {
    trackingData.arc && trackingData.arc.destroy();
    trackingData.circle && trackingData.circle.destroy();
    trackingData.rLine && trackingData.rLine.destroy();
    trackingData.rLabel && trackingData.rLabel.destroy();

    assisGroup.clear();
    SceneGraph.emptyGroup(assisGroup);
    ctx2.viewer.requestRender();

    updateCmdHighlightId('');
  };
  const resetParams = () => {
    faceCSys = null;
    mouseVW = null;
    mousePoint = {
      x: 0,
      y: 0,
    };
    trackingData = {
      circle: null,
      arc: null,
      rLine: null,
      rLabel: null,
    };
  };
  // ========= 释放资源 =========

  const finishDraw = async () => {
    if (radius != 0) {
      if (trackingData.arc.getClockWise()) {
        await wsApi.createArc(centerPoint, endPoint, startPoint, [
          planeNormal.x,
          planeNormal.y,
          planeNormal.z,
        ]);
      } else {
        await wsApi.createArc(centerPoint, startPoint, endPoint, [
          planeNormal.x,
          planeNormal.y,
          planeNormal.z,
        ]);
      }
    }
  };

  const preview = () => {
    if (currentStatus === State.DRAW_CIRCLE) {
      // 绘制圆
      if (!trackingData.circle) {
        if (!trackingData.circle) {
          trackingData.circle = new CSCircle({
            center: centerPoint.clone(),
            radius: radius,
            csys: faceCSys,
            lineWidth: 4,
          });
        }
        trackingData.circle.addTo(assisGroup);
      } else {
        trackingData.circle.update({ radius: radius });
      }
      // 画辅助图形
      drawAssist(centerPoint, startPoint);
    } else if (currentStatus === State.DRAW_ARC) {
      if (startPoint) {
        // 计算角度（需转到二维平面内）
        if (!startPoint2D) {
          startPoint2D = new Vector2().fromArray(
            faceCSys.inTransformation.apply(startPoint.clone()).toArray(),
          );
        }
        if (!centerPoint2D) {
          centerPoint2D = new Vector2().fromArray(
            faceCSys.inTransformation.apply(centerPoint.clone()).toArray(),
          );
        }
        endPoint2D = new Vector2().fromArray(
          faceCSys.inTransformation.apply(endPoint.clone()).toArray(),
        );
        if (!startAngle) {
          startAngle = new Vector2()
            .subVectors(startPoint2D, centerPoint2D)
            .normalize()
            .angle();
        }

        const endAngle = new Vector2()
          .subVectors(endPoint2D, centerPoint2D)
          .normalize()
          .angle();

        startPoint = calcPointWithDistance(centerPoint, startPoint, radius);
        endPoint = calcPointWithDistance(centerPoint, endPoint, radius);
        const params = {
          startPoint,
          center: centerPoint,
          radius: radius,
          startAngle,
          endAngle,
          csys: faceCSys,
          clockWise: true,
          lineWidth: 4,
        };
        // 绘制弧形
        if (!trackingData.arc) {
          trackingData.arc = new CSArc(params);
          trackingData.arc.addTo(assisGroup);
        } else {
          if (startPoint.distanceTo(endPoint) < radius / 50) {
            // 终止点靠近起始点时，重新计算顺逆时针方向
            clockWise = undefined;
          }
          if (clockWise == undefined) {
            clockWise = isClockwise([startPoint2D, endPoint2D, centerPoint2D]);
          }
          trackingData.arc.update({ endAngle, clockWise });
        }
        // 画辅助图形
        drawAssist(centerPoint, endPoint);
      }
    }
    ctx2.viewer.requestRender();
  };

  // 计算是否是顺逆时针
  function isClockwise(points: Array<Vector2>) {
    const p12 = points[1].clone().sub(points[0]);
    const p23 = points[2].clone().sub(points[1]);
    const k = p12.cross(p23);
    if (k > 0) {
      // 顺时针
      return false;
    } else if (k < 0) {
      // 逆时针
      return true;
    }
    return true;
  }

  return (
    <CommandHint
      status={CommandHintStatus.INFO}
      content={hits}
    />
  );
}

export const cmdArc: BaseCommand = {
  id: 'CmdArc',
  label: 'command.arc',
  icon: 'commands/feature/arc',
  create: (label: string, onClose: () => void) => (
    <ArcCommand
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
